/*
 * Sniff.java
 *
 * Created on August 21, 2007, 5:55 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package org.xeerkat.xmpp.tools;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import org.infoset.xml.Document;
import org.infoset.xml.DocumentLoader;
import org.infoset.xml.Element;
import org.infoset.xml.Item;
import org.infoset.xml.ItemDestination;
import org.infoset.xml.XMLException;
import org.infoset.xml.sax.SAXDocumentLoader;
import org.infoset.xml.util.WriterItemDestination;
import org.infoset.xml.util.XMLWriter;
import org.xeerkat.xmpp.ClientConnection;
import org.xeerkat.xmpp.FormField;
import org.xeerkat.xmpp.FormOption;
import org.xeerkat.xmpp.ID;
import org.xeerkat.xmpp.MultiUserChat;
import org.xeerkat.xmpp.Protocol;
import org.xeerkat.xmpp.StanzaDestination;
import org.xeerkat.xmpp.StanzaRouter;

/**
 *
 * @author alex
 */
public class MUC
{
   
   static Logger LOG = Logger.getLogger(Sniff.class.getName());
   /** Creates a new instance of Sniff */
   public MUC()
   {
   }

   public static String readData(BufferedReader reader)
      throws IOException
   {
      String line = null;
      StringBuilder builder = new StringBuilder();
      do {
         line = reader.readLine();
         if (!line.trim().equals(";")) {
            builder.append(line);
            builder.append('\n');
         }
      } while (!line.trim().equals(";"));
      return builder.toString();
   }

   public static Element readElement(DocumentLoader loader,String data)
      throws IOException,XMLException
   {
      //System.out.println(data);
      Document doc = loader.load(new StringReader("<nop xmlns='jabber:client'>"+data+"</nop>"));
      //System.out.println(doc.getDocumentElement().getName().toString()+" "+doc.getDocumentElement().size());
      Iterator<Element> elements = doc.getDocumentElement().getElementChildren();
      return elements.hasNext() ? elements.next() : null;
   }
   
   public static void main(String [] args)
   {
      if (args.length<2) {
         System.err.println("Usage: java -cp xeerkat-xmpp-client.jar org.xeerkat.xmpp.tools.MUC {sender} {sender-password}");
         System.exit(1);
      }
      try {
         ID id = new ID(args[0]);
         id.setPassword(args[1]);
         final ClientConnection connection = ClientConnection.lookupByDomain(id.getDomain());
         connection.connect(id);

         StanzaRouter router = new StanzaRouter();
         connection.startReceiver(router);
         router.addDefaultRoute(new ItemDestination() {
            int level = 0;
            StringWriter w;
            ItemDestination writer;
            public void send(Item item)
               throws XMLException
            {
               switch (item.getType()) {
                  case ElementItem:
                     if (level==0) {
                        w = new StringWriter();
                        writer = new WriterItemDestination(w,"UTF-8");
                     }
                     level++;
                     break;
               }
               writer.send(item);
               switch (item.getType()) {
                  case ElementEndItem:
                     level--;
                     if (level==0) {
                        LOG.info(w.toString());
                        w = null;
                        writer = null;
                     }
               }
            }
         });
         connection.sendPresence(true);

         /* not necessary ... but interesting
         final Discovery discovery = new Discovery(connection,router);
         final ID conference = new ID("conference."+connection.getID().getDomain());
         final ID domain = new ID(connection.getID().getDomain());
         final List<ID> mucEnabled = new ArrayList<ID>();
         final Semaphore discoveryWait = new Semaphore(1);
         discovery.addNotifier(new Discovery.Notifier() {
            public void onDiscovery() {
               if (discovery.getFeatures().contains(MultiUserChat.FEATURE) && !mucEnabled.contains(domain)) {
                  mucEnabled.add(domain);
               }
               if (discovery.getFeatures(conference).contains(MultiUserChat.FEATURE) && !mucEnabled.contains(conference)) {
                  mucEnabled.add(conference);
               }
               discoveryWait.release();
            }
         });
         discoveryWait.acquire();
         discovery.discover();

         discoveryWait.acquire();
         LOG.info("Domain discovery completed.");

         discovery.discover(conference);
         
         discoveryWait.acquire();
         LOG.info("Conference discovery completed.");
         ID mucID = null;
         if (mucEnabled.contains(conference)) {
            LOG.info("Using conference: "+conference);
            mucID = conference;
         } else if (mucEnabled.contains(domain)) {
            LOG.info("Using domain: "+domain);
            mucID = domain;
         } else {
            LOG.info("No MUC enabled service.");
            connection.sendPresence(false);
            connection.disconnect();
            return;
         }
          */

         connection.startKeepAlive();

         BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
         boolean stop = false;
         System.out.println("Online!");

         List<Element> additionalInfo = new ArrayList<Element>();

         MultiUserChat MUC = new MultiUserChat(connection,router) {
            public void onJoin() {
               if (isInRoom()) {
                  System.out.println("Role: "+getRole()+", affiliation: "+getAffiliation());
               } else {
                  System.err.println("Cannot join room.");
               }
            }

            public void onLeave() {
               System.out.println("Left room "+getRoom());
            }
            
            public void onCreateRoom() {
               if (getAffiliation().equals("owner")) {
                  System.out.println("Please configure the room.");
               }
            }

            public void onRoomConfigured() {
               if (isConfigured()) {
                  System.out.println("Configuration accepted.");
               } else {
                  System.err.println("Configuration not accepted, error: "+getLastError());
               }
            }

            public void onRoomConfiguration(Map<String,FormField> configuration) {
               if (configuration==null) {
                  return;
               }
               for (String var : configuration.keySet()) {
                  FormField field = configuration.get(var);
                  String options = "";
                  if (field.getOptions()!=null) {
                     for (FormOption option : field.getOptions()) {
                        options += options.length()==0 ? " { " : ", ";
                        options += '"'+option.getLabel()+"\"="+option.getValue();
                     }
                     options += " }";
                  }
                  System.out.println(field.getLabel()+" "+var+" = "+(field.getValue()==null ? "<null>" : field.getValue())+options);
               }
            }
         };

         MUC.setMessageDestination(new StanzaDestination() {
            public void onStanza() {
               Element message = getStanza();
               Iterator<Element> elements = message.getElementChildren();
               String subject = null;
               String body = null;
               try {
                  StringWriter w = new StringWriter();
                  XMLWriter.writeElement(message, w);
                  System.out.println(message.getAttributeValue("from")+": "+w.toString());
               } catch (Exception ex) {
                  ex.printStackTrace();
               }
               while (elements.hasNext()) {
                  Element child = elements.next();
                  if (child.getName().equals(Protocol.BODY)) {
                     body = child.getText();
                  } else if (child.getName().equals(Protocol.SUBJECT)) {
                     subject = child.getText();
                  } else {
                     try {
                        StringWriter w = new StringWriter();
                        XMLWriter.writeElement(child, w);
                        System.out.println(message.getAttributeValue("from")+": "+w.toString());
                     } catch (Exception ex) {
                        ex.printStackTrace();
                     }
                  }
               }

               if (body!=null && subject!=null) {
                  System.out.println(message.getAttributeValue("from")+": "+subject+", "+body);
               } else if (body != null) {
                  System.out.println(message.getAttributeValue("from")+": "+body);
               } else if (subject != null) {
                  System.out.println(message.getAttributeValue("from")+": "+subject);
               }
            }
         });
         
         DocumentLoader loader = new SAXDocumentLoader();
         do {
            String line = reader.readLine();
            if (line==null) {
               continue;
            }
            line = line.trim();
            if (line==null) {
               continue;
            }
            String [] parts = line.split("\\s+");
            if (parts[0].equals("exit")) {
               stop = true;
            } if (parts[0].startsWith("join")) {
               int atSign = parts[1].indexOf('@');
               if (atSign<=0) {
                  System.err.println("Bad JID of conference: "+parts[1]);
                  continue;
               }
               String name = parts[1].substring(0,atSign);
               String domain = parts[1].substring(atSign+1);
               String nickname = connection.getBindID().getName();
               int slash = domain.indexOf('/');
               if (slash>0) {
                  nickname = domain.substring(slash+1);
                  domain = domain.substring(0,slash);
               }
               MUC.joinRoom(name,domain,nickname,additionalInfo);
            } if (parts[0].startsWith("message")) {
               MUC.sendMessage(line.substring(8));
            } if (parts[0].startsWith("leave")) {
               MUC.leave();
            } if (parts[0].startsWith("configure")) {
               if (parts.length==1) {
                  continue;
               }
               if (parts[1].equals("instant")) {
                  MUC.configureRoom(null);
                  continue;
               }
               List<FormField> fields = new ArrayList<FormField>();
               boolean valid = true;
               FormField formType = new FormField("FORM_TYPE");
               formType.setValue("http://jabber.org/protocol/muc#roomconfig");
               fields.add(formType);
               for (int i=1; i<parts.length; i++) {
                  int eq = parts[i].indexOf('=');
                  if (eq<0) {
                     System.err.println("Invalid configuration item (no equals): "+parts[i]);
                     valid = false;
                     continue;
                  }
                  String var = parts[i].substring(0,eq).trim();
                  String value = parts[i].substring(eq+1).trim();
                  FormField field = new FormField(var);
                  field.setValue(value);
                  fields.add(field);
               }
               if (valid) {
                  MUC.configureRoom(fields);
               }
            } if (parts[0].startsWith("get")) {
               if (parts.length==1) {
                  continue;
               }
               if (parts[1].equals("configuration")) {
                  MUC.getRoomConfiguration();
               }

            } if (parts[0].equals("info")) {
              if (parts.length>1) {
                  if (parts[1].equals("clear")) {
                     additionalInfo.clear();
                     continue;
                  } else if (parts[1].equals("show")) {
                     for (Element info : additionalInfo) {
                        try {
                           StringWriter w = new StringWriter();
                           XMLWriter.writeElement(info, w);
                           System.out.println(w.toString());
                        } catch (Exception ex) {
                           ex.printStackTrace();
                        }
                     }
                     continue;
                  } else if (parts[1].charAt(0)=='<') {
                     Element info = readElement(loader,line.substring(4));
                     if (info==null) {
                        System.err.println("No element produced!");
                        continue;
                     }
                     additionalInfo.add(info);
                  }
               } else {
                  String data = readData(reader);
                  Element info = readElement(loader,data);
                  if (info==null) {
                     System.err.println("No element produced!");
                     continue;
                  }
                  additionalInfo.add(info);
               }
            } if (parts[0].equals("data")) {
               Element data = null;
               if (parts.length>1) {
                  if (parts[1].charAt(0)=='<') {
                     data = readElement(loader,line.substring(4));
                     if (data==null) {
                        System.err.println("No element produced!");
                        continue;
                     }
                  } else {
                     System.err.println("Invalid command.");
                     continue;
                  }
               } else {
                  String xmlData = readData(reader);
                  data = readElement(loader,xmlData);
                  if (data==null) {
                     System.err.println("No element produced!");
                     continue;
                  }
               }
               MUC.sendMessage("data",data);

            } if (parts[0].equals("send")) {

               String data = readData(reader);

               final ItemDestination dest = connection.getStanzaDestination();

               loader.generate(new StringReader("<nop xmlns='jabber:client'>"+data+"</nop>"),
                  new ItemDestination() {
                     int level = 0;
                     public void send(Item item)
                        throws XMLException
                     {
                        switch (item.getType()) {
                           case DocumentItem:
                           case ElementItem:
                              level++;
                              if (level==2) {
                                 return;
                              }
                              dest.send(item);
                              break;
                           case ElementEndItem:
                           case DocumentEndItem:
                              level--;
                              if (level==1) {
                                 return;
                              }
                           default:
                              dest.send(item);
                        }
                     }
                  }
               );
            }
         } while (!stop);
         connection.sendPresence(false);
         connection.stopKeepAlive();
         System.out.println("Disconnecting...");
         connection.disconnect();
         connection.stopReceiver();
         System.out.println("Goodbye.");
      } catch (Exception ex) {
         ex.printStackTrace();
      }
   }
   
}
